package com.hanson.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrBuilder;
import com.hanson.annotation.Log;
import com.hanson.common.api.CommonResult;
import com.hanson.common.api.ResultCode;
import com.hanson.common.constant.Constant;
import com.hanson.common.exception.DmallException;
import com.hanson.mapper.UserMapper;
import com.hanson.mapper.UserRoleMapper;
import com.hanson.platform_api.properties.TencentCOSProperties;
import com.hanson.pojo.vo.User;
import com.hanson.pojo.vo.UserRole;
import com.hanson.utils.RedisUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @description: 腾讯云COS对象存储控制器
 * @classname: TencentCOSController
 * @author: Hanson
 * @create: 2021/08/07
 **/
@Slf4j
@Api(tags = "腾讯云COS对象存储服务接口")
@RestController
@Log
@RequestMapping("/dmall/tencent/cos")
public class TencentCOSController {

    @Autowired
    TencentCOSProperties tencentCOSProperties;
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserRoleMapper userRoleMapper;
    @Autowired
    RedisUtil redisUtil;


    @ApiOperation("商品图片上传")
    @PostMapping("/goods/image/upload")
    @ResponseBody
    public CommonResult<Object> imageUpload(MultipartFile multipartFile){
        if (multipartFile == null){
            throw new DmallException("上传文件不能为空！");
        }

        /*
        ----------身份验证,非管理员上传到temp临时文件夹-----------
         */
        String filePath = "temp";
        User principal = (User)(SecurityUtils.getSubject().getPrincipal());
        if (principal == null){
            throw new DmallException("非法用户访问！");
        }
        String userId = principal.getUserId();
        List<UserRole> userRoles = userRoleMapper.doGetUserRolesByUid(userId);
        //超级管理员角色
        Set<Integer> collect = userRoles.stream().map(UserRole::getRoleId).filter((d) -> d == 1).collect(Collectors.toSet());
        if (collect.size() == 1){
            filePath = "openfile";
        }
        /*
        ----------身份验证,非管理员上传到temp临时文件夹-----------
         */
        /**
         * 恶意刷cos流量容量校验
         */
        CommonResult<Object> filter = imageFilter(multipartFile);
        if (!(filter.getCode() == 200)){
            return filter;
        }
        //流量控制,guest体验用户10s上传一次
        if (Constant.GUEST_USER.equals(principal.getUserName())){
            String lastUpload = redisUtil.get("upload:limit:" + Constant.GUEST_USER);
            if (StringUtils.isNotBlank(lastUpload)){
                return new CommonResult<>(ResultCode.UPLOAD_LIMIT,null,"体验用户上传频率：1次/10秒");
            }
            redisUtil.setEx("upload:limit:" + Constant.GUEST_USER, DateUtil.now(),10, TimeUnit.SECONDS);
        }

        COSClient cosClient = getOSSClient(tencentCOSProperties);
        File file = null;
        try{
            file = File.createTempFile("temp",null);
            multipartFile.transferTo(file);
            // 指定要上传到的存储桶
            String bucketName = tencentCOSProperties.getBucketName();
            String fileName = multipartFile.getOriginalFilename();
            String fileHost = tencentCOSProperties.getFileHost();
            String path = formatPath(formatPath(fileHost, filePath), fileName);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, path, file);
            PutObjectResult putObjectResult = getOSSClient(tencentCOSProperties).putObject(putObjectRequest);

            String url = new StrBuilder(tencentCOSProperties.getPath()).append(path).toString();
            System.out.println(url);
            return new CommonResult<>(url);
        } catch (IOException e) {
            log.error("商城上传图片IO异常",e);
        }finally {
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }
        return null;
    }


    /**
     *
     * 获取腾讯云COS存储对象
     *
     * @params: [tencentCOSProperties]
     * @param: tencentCOSProperties
     * @return: {@link COSClient}
     * @since: 2021/1/13
     **/
    public COSClient getOSSClient(TencentCOSProperties tencentCOSProperties){
        // 1 初始化用户身份信息（secretId, secretKey）。
        COSCredentials cred = new BasicCOSCredentials(tencentCOSProperties.getAccessKey(), tencentCOSProperties.getSecretKey());
        // 2 设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
        Region region = new Region(tencentCOSProperties.getRegion());
        ClientConfig clientConfig = new ClientConfig(region);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);
        return cosClient;
    }

    public TencentCOSProperties getTencentCOSProperties(){
        return this.tencentCOSProperties;
    }

    public String formatPath(String path,String fileName){
        StrBuilder strBuilder = new StrBuilder();
        if (StringUtils.isBlank(path)){
            return strBuilder.append("/").append(fileName).toString();
        }
        if (!path.startsWith("/") && !path.startsWith("https")){
            strBuilder.append("/");
        }
        if (!path.endsWith("/")){
            strBuilder.append(path).append("/").append(fileName);
        }else {
            strBuilder.append(path).append(fileName);
        }
        return strBuilder.toString();
    }

    /**
     * 上传图片后缀校验
     * @param file
     * @return
     */
    public boolean suffixFilter(MultipartFile file){
        String reg = "(jpg|jpeg|png|tiff|tif|webp|heif|gif|bmp|psd|svg|ai|cdr|eps|dng)";
        Pattern pattern = Pattern.compile(reg);
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) return false;
        int index = originalFilename.lastIndexOf(".");
        if (index >= 0){
            originalFilename = originalFilename.substring(++index);
            System.out.println(originalFilename);
        }
        return pattern.matcher(Objects.requireNonNull(originalFilename).toLowerCase(Locale.ROOT)).matches();
    }

    /**
     * 上传文件大小校验
     * @param file
     * @return
     */
    public boolean sizeFilter(MultipartFile file){
        long size = file.getSize();
        if (size <= 0) return false;
        //10M limit
        return size/(1024 * 1024d) < 10;
    }

    public CommonResult<Object> imageFilter(MultipartFile file){
        boolean suffixFilter = suffixFilter(file);
        boolean sizeFilter = sizeFilter(file);
        CommonResult<Object> result = new CommonResult<>();
        if (!suffixFilter){
            result.setCode(ResultCode.INVALID.getCode());
            result.setType(ResultCode.INVALID.getType());
            result.setContent("上传图片格式不支持！支持格式：(jpg|jpeg|png|tiff|tif|webp|heif|gif|bmp|psd|svg|ai|cdr|eps|dng)。");
        }
        if (!sizeFilter){
            result.setCode(ResultCode.INVALID.getCode());
            result.setType(ResultCode.INVALID.getType());
            String ors = "";
            if (result.getCode() == ResultCode.INVALID.getCode())
                ors = result.getContent();
            result.setContent( ors + "上传图片大小最大为10M！");
        }
        return result;
    }
}
